
package com.turing.draw.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.turing.common.constants.CommonConstants;
import com.turing.common.constants.FileType;
import com.turing.common.entity.Context;
import com.turing.common.entity.UserToken;
import com.turing.common.response.ResponseVO;
import com.turing.common.util.FileTypeUtil;
import com.turing.common.util.SnowflakeIdWorker;
import com.turing.common.util.Tools;
import com.turing.common.util.uncompress.AbstractUncompress;
import com.turing.common.util.uncompress.factory.UncompressFactory;
import com.turing.draw.client.preprocessor.PreProcessorClient;
import com.turing.draw.dao.DrawTaskMapper;
import com.turing.draw.model.domain.DrawTask;
import com.turing.draw.model.domain.FileTask;
import com.turing.draw.model.vo.TemplateVo;
import com.turing.draw.model.vo.UploadVo;
import com.turing.draw.model.vo.in.TaskRecordVo;
import com.turing.draw.service.OcrPageTaskService;
import com.turing.draw.vo.out.DrawTaskVO;
import com.turing.draw.client.procedure.ProcedureInnerService;
import com.turing.draw.client.template.TemplateClient;
import com.turing.draw.enums.TaskStatusEnum;
import com.turing.draw.service.FileTaskService;
import com.turing.draw.service.DrawTaskService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

/**
 * @Description 服务类实现
 * @Author lcy
 * @Date 2021-10-25
 */
@Service
public class DrawTaskServiceImpl extends ServiceImpl<DrawTaskMapper, DrawTask> implements DrawTaskService {

    private static final Log logger = LogFactory.getLog(DrawTaskServiceImpl.class);

    @Value("${ocr.file.path}")
    private String OCR_FILE_PATH;

    @Autowired
    private DrawTaskMapper drawTaskMapper;

    @Autowired
    private FileTaskService fileTaskService;

    @Autowired
    private TemplateClient templateClient;

    @Autowired
    private ProcedureInnerService procedureInnerService;

    @Autowired
    private OcrPageTaskService ocrPageTaskService;

    @Autowired
    private PreProcessorClient preProcessorClient;

    @Override
    public UploadVo uploadFileAndCreateTask(MultipartFile[] files, String templateId, Integer needMerge, int pageNumber) throws IOException {

        //错误的文件名
        List<String> errorTaskNames = new ArrayList<>();

        Set<String> errorTaskNameSet = Tools.isNotEmpty(errorTaskNames) ? new HashSet<>(errorTaskNames) : new HashSet<>();

        //创建orc流程
        if (needMerge == 0) {
            logger.info(">>>>>>>> don`t needMerge is " + needMerge);
            for (MultipartFile file : files) {
                if (!errorTaskNameSet.contains(file.getOriginalFilename())) {
                    MultipartFile[] nonMergedFiles = new MultipartFile[1];
                    nonMergedFiles[0] = file;
                    createFileTask(nonMergedFiles, templateId, pageNumber);
                }
            }
        } else {
            // 合并抽取
            if (!errorTaskNameSet.contains(files[0].getOriginalFilename())) {
                logger.info(">>>>>>> needMerge is " + needMerge);
                checkFileName(files);
                createFileTask(files, templateId, pageNumber);
            }
        }
        logger.info("======>上传完成，场景为："+templateId);
        return UploadVo.builder().copyTaskNames(new ArrayList<>()).errorTaskNames(errorTaskNames).build();

    }

    /**
     * 要素抽取 | 选择场景创建任务
     * @param files
     * @param templateId
     * @param pageNumber
     * @throws IOException
     */
    @Transactional(rollbackFor = Exception.class)
    public void createFileTask(MultipartFile[] files, String templateId, Integer pageNumber) throws IOException {
        logger.info(">>>>>开始创建任务");
        // prepare params to initialize task records
        List<TaskRecordVo> taskRecordVos = new ArrayList<>();
        if (pageNumber == 0) {

            SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker();
            String taskSn = String.valueOf(snowflakeIdWorker.nextId());
            String dirPath = mkdirs(taskSn);
            // save files
            logger.info("saving files...");
            List<String> filePaths = new ArrayList<>();
            List<String> compressType = Arrays.asList(FileType.ZIP, FileType.SEVENZ,FileType.RAR);
            for (MultipartFile file : files) {
                File transFile = convert2TransFile(dirPath, file);
                String fileType = FileTypeUtil.getTypeByStream( file.getInputStream() );
                if( compressType.contains( fileType ) ){
                    String packageName = UUID.randomUUID().toString();
                    String uncompressFileDir = dirPath+"//"+packageName+"//";
                    AbstractUncompress uncompress = UncompressFactory.createUncompress( fileType );
                    uncompress.uncompress(transFile.getAbsolutePath(),uncompressFileDir);
                    File dir = new File(uncompressFileDir);
                    if(dir.exists() && dir.isDirectory()){
                        ergodicDirFiles(filePaths,dir);
                    }
                }else{
                    filePaths.add(transFile.getAbsolutePath());
                }
            }
            doCreateTaskRecord(templateId, filePaths,taskRecordVos);
            logger.info("deleting mid folder:"+(new File(dirPath)).delete());
        } else {
            byte[] bytes = files[0].getBytes();
            PdfReader reader = new PdfReader(bytes);
            int totalPage = reader.getNumberOfPages();
            int totalNum = totalPage % pageNumber == 0 ? totalPage / pageNumber : totalPage / pageNumber + 1;
            for (int i = 0; i < totalNum; i++) {
                Document document = null;
                String taskSn = UUID.randomUUID().toString();
                String dirPath = mkdirs(taskSn);

                int start = i * pageNumber + 1;
                int end = Math.min((i + 1) * pageNumber, totalPage);
                String taskName = files[0].getOriginalFilename();
                assert taskName != null;
                if (taskName.contains(".")) {
                    String prefix = taskName.substring(0, taskName.lastIndexOf("."));
                    String suffix = taskName.substring(taskName.lastIndexOf("."));
                    taskName = prefix + "_" + i + suffix;
                } else {
                    taskName = taskName + "_" + i;
                }
                List<String> filePaths = new ArrayList<>();
                filePaths.add(dirPath + taskName);
                logger.info("splitting pdf files,index is: "+ i +",page range:" + start + " to "+ end);
                PdfCopy copy;
                try {
                    document = new Document(reader.getPageSize(1));
                    copy = new PdfCopy(document, new FileOutputStream(dirPath + taskName));
                    document.open();
                    for (int j = start; j <= end; j++) {
                        document.newPage();
                        PdfImportedPage page = copy.getImportedPage(reader, j);
                        copy.addPage(page);
                    }
                } catch (DocumentException | FileNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    assert document != null;
                    document.close();
                }

                doCreateTaskRecord(templateId, filePaths,taskRecordVos);

                // sweep native files
                logger.info("deleting mid folder:"+(new File(dirPath)).delete());
            }
        }
        logger.info("----->taskRecordVos="+taskRecordVos);
        ResponseVO<Object> taskRecords = procedureInnerService.createTaskRecords(taskRecordVos);
        logger.info(">>>>>>>>内部调用 procedure 创建task_record任务，返回参数为："+taskRecords);
    }

    public static void ergodicDirFiles( List<String> paths,File dir ){

        File[] files = dir.listFiles();
        if(files != null && files.length > 0){
            for (File file : files) {
                if(file.isDirectory()){
                    ergodicDirFiles(paths,file);
                }else{
                    paths.add(file.getAbsolutePath());
                }
            }
        }

    }


    public static void main(String[] args) {

        File file = new File("D:\\environment\\111");
        List<String> aaa = new ArrayList<>();
        ergodicDirFiles(aaa,file);
        System.out.println(aaa);

    }

    private File convert2TransFile(String dirPath, MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename();
        if (Tools.isEmpty(fileName)) {
            fileName = file.getName();
        }
        File transFile = new File(dirPath + fileName);

        if (!transFile.exists()) {
            boolean newFile = transFile.createNewFile();
        }

        try {
            // get bytes
            byte [] bytes = file.getBytes();
            // write bytes
            OutputStream out = new FileOutputStream(transFile);

            out.write(bytes);
            out.close();
            transFile.deleteOnExit();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return transFile;
    }

    @Transactional
    public void doCreateTaskRecord( String templateId, List<String> filePaths,List<TaskRecordVo> taskRecordVos ) {

        ResponseVO<TemplateVo> resVo = templateClient.getTemplateById(templateId);

        TemplateVo templateVo = resVo.getData();

        String originalFilename = new File(filePaths.get(0)).getName();

        SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(0, 1);
        String taskSn = String.valueOf(snowflakeIdWorker.nextId());

        logger.info("saving task record,taskSn is:"+taskSn);
        DrawTask drawTask = DrawTask.builder().taskName(originalFilename)
                .templateId(templateId)
                .templateGroup(templateVo.getTemplateGroup())
                .templateName(templateVo.getTemplateName())
                .fileNumbers(filePaths.size())
                .taskStatus(TaskStatusEnum.UPLOADED.getCode())
                .taskSn(taskSn)
                .build();
        UserToken tokenContext = Context.getTokenContext();
        Integer userId = tokenContext.getUserId();
        String userName = tokenContext.getUserName();
        logger.info("tokenContext is:"+tokenContext);
        logger.info("userName is:"+userName);
        drawTask.setCreatedBy(userId);
        int insert = drawTaskMapper.insert(drawTask);
        TaskRecordVo taskRecordVo = TaskRecordVo.builder().taskSn(taskSn).taskName(originalFilename).templateId(templateId).templateName(templateVo.getTemplateName())
                .templateGroup(templateVo.getTemplateGroup()).createdBy(userId).createdByRole(tokenContext.getRoleId()).createdByName(userName).build();
        taskRecordVos.add(taskRecordVo);

        // avoid duplicated
        if(insert == 0){
            drawTask.setTaskSn(taskSn);
            drawTaskMapper.insert(drawTask);
        }

        filePaths.forEach(filePath -> {
            fileTaskService.createFileTask(templateId,drawTask, filePath);
        });

    }

    /**
     * 校验重复名称--只针对合并抽取
     *
     * @param files 文件数组
     * @author lcy
     * @date 2021/9/26 15:06
     **/
    public void checkFileName(MultipartFile[] files) {
        //存储重名的map
        Map<String, Integer> nameMap = new HashMap<>(files.length);
        for (int i = 0; i < files.length; i++) {
            MultipartFile file = files[i];
            if ((file != null && !file.isEmpty())) {
                //获取文件名
                String filename = Tools.isNotEmpty(file.getOriginalFilename()) ? file.getOriginalFilename() : file.getName();
                //文件名称不包含后缀
                String prefixName = filename.substring(0, filename.lastIndexOf('.'));
                if (Tools.isNotEmpty(nameMap.get(prefixName))) {
                    //计算下标防止多个文件重复
                    Integer index = nameMap.get(prefixName);
                    String newPrefixName = prefixName + "_" + index;
                    //文件类型
                    String fileType = filename.substring(filename.lastIndexOf('.'));
                    try (InputStream inputStream = file.getInputStream()) {
                        files[i] = new MockMultipartFile(newPrefixName + fileType, inputStream);
                    } catch (IOException e) {
                        logger.error("重名文件替换失败 ", e);
                    }
                    nameMap.put(prefixName, index + 1);
                } else {
                    nameMap.put(prefixName, 1);
                }
            }
        }

    }

    @Override
    public DrawTask getTaskRecord(String taskSn) {
        return new LambdaQueryChainWrapper<>(drawTaskMapper).eq(DrawTask::getTaskSn, taskSn)
                .eq(DrawTask::getIsDel, 0)
                .one();
    }

    @Override
    public DrawTaskVO getTaskFilePath(String taskSn) {

        DrawTaskVO resultVO = new DrawTaskVO();

        DrawTask drawTask = lambdaQuery().eq(DrawTask::getTaskSn,taskSn).one();
        if ( drawTask != null ) {

            resultVO.setFileUrl("");
            resultVO.setTaskName(drawTask.getTaskName());
            resultVO.setTaskSn(drawTask.getTaskSn());
            resultVO.setTotalPage(0);
            resultVO.setStatus(getReviewStatus(drawTask.getTaskStatus()));
            String templateId = drawTask.getTemplateId();
            String templateName = templateClient.getTemplateById(templateId).getData().getTemplateName();
            resultVO.setTemplateName(templateName);
            resultVO.setTemplateId(drawTask.getTemplateId());

            List<FileTask> fileTasks = fileTaskService.lambdaQuery().eq(FileTask::getParentTaskSn,taskSn).list();

            List<DrawTaskVO> childTasks = new ArrayList<>();

            if (fileTasks != null && fileTasks.size() > 0) {
                for (FileTask task : fileTasks) {
                    DrawTaskVO child = new DrawTaskVO();
                    child.setFileUrl(task.getFileUrl());
                    //                    child.setTaskName(task.getTaskName());
                    child.setTaskName(task.getFileName());
                    child.setTaskSn(task.getTaskSn());
                    child.setTotalPage(task.getTotalPage());
                    //child.setOriginTextPath(task.getFileOriginText());
                    child.setJsonUrl(task.getJSONUrl());
                    childTasks.add(child);
                }
            }
            resultVO.setChildTasks(childTasks);

        }

        return resultVO;

    }

    @Override
    public boolean updateTaskByStatus(String parentTaskSn, Integer status) {
        return lambdaUpdate()
                .eq(DrawTask::getTaskSn, parentTaskSn)
                .set(DrawTask::getTaskStatus, TaskStatusEnum.PRETASKFINISHED.getCode())
                .set(DrawTask::getUpdatedBy, Context.getTokenContext().getUserId())
                .update();
    }

    private String mkdirs(String taskSn) {

        File OCR_DIR = new File(OCR_FILE_PATH);
        if (OCR_DIR.exists()) {
            OCR_DIR.mkdirs();
        }

        String dirPath = OCR_FILE_PATH + taskSn + "/";
        File taskDir = new File(dirPath);
        if (!taskDir.exists()) {
            taskDir.mkdirs();
        }
        return dirPath;

    }

    @Override
    public String getTaskTemplate(String taskSn) {

        DrawTask drawTask = lambdaQuery().eq(DrawTask::getTaskSn,taskSn).last("limit 1").one();
        return drawTask == null ? CommonConstants.EMPTY_STRING : drawTask.getTemplateId();

    }

    @Override
    public Boolean removeBatch(List<String> taskSns) {

        taskSns.forEach(ocrPageTaskService::deleteByParentTaskSn);
        fileTaskService.remove(new LambdaQueryWrapper<FileTask>().in(FileTask::getParentTaskSn,taskSns));
        preProcessorClient.removeTasks(taskSns);
        return remove(new LambdaQueryWrapper<DrawTask>().in(DrawTask::getTaskSn,taskSns));

    }

    private Integer getReviewStatus(Integer status) {
        switch (status){
            case 0:
            case 1:
            case 2:
                return -1;
            case 4:
                return 1;
            case 5:
                return 2;
            case 10:
                return 10;
            default:
                return 0;
        }
    }

}
